#include "spk_camera.h"
#include "config.h"
#include "video.h"
#include "video_osd.h"

#define LOG_TAG "video_osd"
#define LOG_LVL LOG_LVL_INFO
#include "spk_ulog.h"

cls_video_osd::cls_video_osd(cls_video *p_video)
{
    video = p_video;

    load_font("font/HZK16", font_lut[1][OSD_SIZE_16]);
    load_font("font/HZK24F", font_lut[1][OSD_SIZE_24]);
    load_font("font/HZK32", font_lut[1][OSD_SIZE_32]);
    load_font("font/HZK48", font_lut[1][OSD_SIZE_48]);

    /*ASCII
    包含12*8, 16*8, 24*12, 32*16, 48*24
    所有的ASC字库都只存储了可显示的ASCII字符，所以计算offset时，要减去32
    12*8和48*24的字库是按行取值，其他是按列取值*/
    load_font("font/ASC16_8", font_lut[0][OSD_SIZE_16]);
    load_font("font/ASC24_12", font_lut[0][OSD_SIZE_24]);
    load_font("font/ASC32_16", font_lut[0][OSD_SIZE_32]);
    load_font("font/ASC48_24", font_lut[0][OSD_SIZE_48]);
}

cls_video_osd::~cls_video_osd()
{
    free_fonts();
}

void cls_video_osd::add_osd(ctx_image_data *img_osd, ctx_image_data *img_org)
{
    init_data(img_osd, img_org);
    // string test_text = "你好ABC.MI";
    // string gb_text = utf8_to_gb2312(test_text); // 由于本cpp的编码为utf-8, 而字库的编码为GB2312, 所以如果需要使用中文, 必须先转换
    // add_yuv422((char *)gb_text.c_str(), 0, 0, OSD_SIZE_48, RGB(255, 0, 255));
    add_yuv422((char *)video->cfg_cur->osd_info[0], video->cfg_cur->osd_info_location[0][0], video->cfg_cur->osd_info_location[0][1], OSD_SIZE_48, RGB(255, 0, 255));
    add_yuv422((char *)video->cfg_cur->osd_info[1], video->cfg_cur->osd_info_location[1][0], video->cfg_cur->osd_info_location[1][1], OSD_SIZE_48, RGB(0, 0, 0));
    add_yuv422((char *)video->cfg_cur->osd_info[2], video->cfg_cur->osd_info_location[2][0], video->cfg_cur->osd_info_location[2][1], OSD_SIZE_48, RGB(0, 255, 0));
    add_yuv422((char *)video->cfg_cur->osd_info[3], video->cfg_cur->osd_info_location[3][0], video->cfg_cur->osd_info_location[3][1], OSD_SIZE_48, RGB(255, 255, 255));
}

void cls_video_osd::load_font(const char *path, u8 *&buffer)
{
    FILE *fp = fopen(path, "rb");
    RT_ASSERT(fp);
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    buffer = new u8[size];
    fseek(fp, 0, SEEK_SET);
    fread(buffer, 1, size, fp);
    fclose(fp);
}

void cls_video_osd::free_fonts()
{
    for (int i = 0; i < 2; i++)
        for (int j = OSD_SIZE_16; j <= OSD_SIZE_48; j++)
        {
            delete font_lut[i][j];
            font_lut[i][j] = nullptr;
        }
}

void cls_video_osd::init_data(ctx_image_data *img_osd, ctx_image_data *img_org)
{
    cols = img_osd->width;
    rows = img_osd->height;

    memcpy(img_osd->data, img_org->data, img_osd->size);
    img = img_osd->data;
}

string cls_video_osd::utf8_to_gb2312(const string &utf8)
{
    iconv_t cd = iconv_open("GB2312", "UTF-8");
    if (cd == (iconv_t)-1)
        return "";

    size_t in_len = utf8.length();
    std::vector<char> out_buf(in_len * 2 + 1, 0);
    char *in_ptr = (char *)utf8.c_str();
    char *out_ptr = out_buf.data();
    size_t out_len = out_buf.size();

    if (iconv(cd, &in_ptr, &in_len, &out_ptr, &out_len) == (size_t)-1)
    {
        iconv_close(cd);
        return "";
    }
    iconv_close(cd);
    return std::string(out_buf.data());
}

int cls_video_osd::add_rgb(char osd_info[], int x, int y, int size, u32 color)
{
    u8 *info = (u8 *)osd_info;

    int _len = strlen(osd_info);

    int csize = 0;

    /*计算字符尺寸*/
    /*如果是汉字，垂直尺寸与横向尺寸相同*/
    /*如果是ASCII码，那这个尺寸是垂直尺寸，横向尺寸要减半*/
    switch (size)
    {
    case OSD_SIZE_16:
        csize = 16;
        break;
    case OSD_SIZE_24:
        csize = 24;
        break;
    case OSD_SIZE_32:
        csize = 32;
        break;
    case OSD_SIZE_48:
        csize = 48;
        break;
    default:
        LOG_E("不支持的OSD尺寸");
        return -1;
    }

    /*确保图像有效*/
    if (img == nullptr)
    {
        LOG_E("图像为空!");
        return -1;
    }

    int err = 0;
    int xstride = 0;
    int ystride = csize;
    int hflag = 0;

    /*确保叠加位置不超过图像边界*/
    if (x + _len * (csize >> 1) > cols)
    {
        int max = (cols - x) / (csize >> 1);
        if (max <= 0)
            _len = 0;
        else
        {
            switch (max)
            {
            case 1:
                _len = (osd_info[0] >= 0) ? 1 : 0;
                break;
            default:
                _len = (osd_info[max - 1] <= 0 && osd_info[max - 2] >= 0) ? max - 1 : max;
                break;
            }
        }
        err = -2;
        LOG_W("叠加位置超过图像X边界，部分字符将被截断!");
    }

    if (y + ystride > rows)
    {

        y = rows - ystride;
        if (y <= 0)
        {
            _len = 0;
            LOG_E("叠加位置超过图像上Y边界，OSD叠加失败!");
            return -3;
        }
        else
        {
            LOG_W("叠加位置超过图像Y下边界，已自动调整!");
            err = -4;
        }
    }

    /*画笔颜色*/
    u8 b = color & 0xFF;
    u8 g = (color >> 8) & 0xFF;
    u8 r = (color >> 16) & 0xFF;

    /*OSD显示在图像中的首地址*/
    u8 *start = img + (y * cols + x) * 3;

    int xoff = 0;

    int xsize = csize;
    int ysize = csize;

    int off = 0;   /*当前字符在字库中的偏移位置*/
    int hz_en = 0; /*当前字符是否是汉字字符*/
    int dir = 0;   /*如果为0,代表字库扫描方向是先横向再纵向，否则是先纵向再横向*/

    for (int c = 0; c < _len; c++)
    {
        /*ASCII码*/
        if (osd_info[c] >= 0)
        {
            /*ASCII码宽度减半*/
            xsize = csize >> 1;
            /*48*24的为横向排列，其他的为纵向排列*/
            dir = (csize == 48) ? 0 : 1;
            /*计算字符在字库中的偏移量*/
            /*这里需要减去前面32个不可打印字符*/
            off = (info[c] - 32) * (ysize * xsize >> 3);
            hz_en = 0;
        }
        /*汉字*/
        else
        {
            /*获得区码与位码*/
            int qh = info[c] - 0xa0;
            int wh = info[c + 1] - 0xa0;
            xsize = csize;
            /*计算该汉字在字库中偏移量*/
            off = (94 * (qh - 1) + (wh - 1)) * (ysize * xsize >> 3);
            hz_en = 1;
            /*汉字库默认都是横向排列*/
            dir = 0;
            /*汉字占用2个字符*/
            c++;
        }

        u8 *lut = font_lut[hz_en][size] + off;
        u8 *p = start + xoff * 3; // 当前字符所要绘制的第一条所在位置的指针

        int pos = 0;

        int off1 = (dir == 0) ? 1 : cols;
        int off2 = (dir == 0) ? (cols - xsize) : ((0 - ysize) * cols + 1);
        int size1 = (dir == 0) ? (xsize >> 3) : (ysize >> 3);
        int size2 = (dir == 0) ? ysize : xsize;

        off1 *= 3;
        off2 *= 3;

        for (int i = 0; i < size2; i++)
        {
            for (int j = 0; j < size1; j++)
            {
                u8 tmp = lut[pos++];
                for (int k = 0; k < 8; k++)
                {
                    if (tmp & 0x80)
                    {
                        *p = r;
                        *(p + 1) = g;
                        *(p + 2) = b;
                    }
                    p += off1;
                    tmp <<= 1;
                }
            }
            p += off2;
        }
        xoff += xsize;
    }

    return err;
}

int cls_video_osd::add_yuv422(char osd_info[], int x, int y, int size, u32 color)
{
    u8 *info = (u8 *)osd_info;

    int _len = strlen(osd_info);

    int csize = 0;

    switch (size)
    {
    case OSD_SIZE_16:
        csize = 16;
        break;
    case OSD_SIZE_24:
        csize = 24;
        break;
    case OSD_SIZE_32:
        csize = 32;
        break;
    case OSD_SIZE_48:
        csize = 48;
        break;
    default:
        LOG_E("不支持的OSD尺寸");
        return -1;
    }

    if (img == nullptr)
    {
        LOG_E("图像为空!");
        return -1;
    }

    int err = 0;
    int xstride = 0;
    int ystride = csize;
    int hflag = 0;

    if (x + _len * (csize >> 1) > cols)
    {
        int max = (cols - x) / (csize >> 1);
        if (max <= 0)
            _len = 0;
        else
        {
            switch (max)
            {
            case 1:
                _len = (osd_info[0] >= 0) ? 1 : 0;
                break;
            default:
                _len = (osd_info[max - 1] <= 0 && osd_info[max - 2] >= 0) ? max - 1 : max;
                break;
            }
        }
        err = -2;
        LOG_W("叠加位置超过图像X边界，部分字符将被截断!");
    }

    if (y + ystride > rows)
    {
        y = rows - ystride;
        if (y <= 0)
        {
            _len = 0;
            LOG_E("叠加位置超过图像上Y边界，OSD叠加失败!");
            return -3;
        }
        else
        {
            LOG_W("叠加位置超过图像Y下边界，已自动调整!");
            err = -4;
        }
    }

    u8 b = color & 0xFF;
    u8 g = (color >> 8) & 0xFF;
    u8 r = (color >> 16) & 0xFF;

    u8 Y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
    u8 U = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
    u8 V = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;

    u8 *start = img + (y * cols + x) * 2;
    int xoff = 0;

    int xsize = csize;
    int ysize = csize;

    int off = 0;
    int hz_en = 0;
    int dir = 0;

    for (int c = 0; c < _len; c++)
    {

        if ((osd_info[c] & 0x80) == 0)
        {

            xsize = csize >> 1;
            dir = (csize == 48) ? 0 : 1;
            off = (info[c] - 32) * (ysize * xsize >> 3);
            hz_en = 0;
        }

        else
        {
            int qh = info[c] - 0xa0;
            int wh = info[c + 1] - 0xa0;
            xsize = csize;
            off = (94 * (qh - 1) + (wh - 1)) * (ysize * xsize >> 3); // each 94
            hz_en = 1;
            dir = 0;
            c++;
        }

        u8 *lut = font_lut[hz_en][size] + off;
        u8 *p = start + xoff * 2; // yuv422 2byte each

        int pos = 0;

        int off1 = (dir == 0) ? 1 : cols;
        int off2 = (dir == 0) ? (cols - xsize) : ((0 - ysize) * cols + 1);
        int size1 = (dir == 0) ? (xsize >> 3) : (ysize >> 3);
        int size2 = (dir == 0) ? ysize : xsize;

        off1 *= 2;
        off2 *= 2;

        for (int i = 0; i < size2; i++)
        {
            for (int j = 0; j < size1; j++)
            {
                u8 tmp = lut[pos++];
                for (int k = 0; k < 8; k++)
                {
                    if (tmp & 0x80)
                    {
                        *p = Y;
                        // 对于YUV422格式，两个Y共享一组UV值
                        // 所以我们在偶数像素位置设置UV值
                        if ((j * 8 + k) % 2 == 0)
                        {
                            *(p + 1) = U; // U值
                            *(p + 3) = V; // V值，注意这里是下一个像素的位置
                        }
                    }
                    p += off1;
                    tmp <<= 1;
                }
            }
            p += off2;
        }
        xoff += xsize;
    }

    return err;
}
